package com.neusoft.neuiotms.dcom.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.notice.constant.NoticeConsts;
import com.neusoft.bizcore.notice.notification.dto.NoticeDingDTO;
import com.neusoft.bizcore.notice.notification.dto.NoticeRecordDTO;
import com.neusoft.bizcore.notice.notification.dto.NoticeRecordSystemDTO;
import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.bizcore.web.utils.EasyPoiExcelUtil;
import com.neusoft.bizcore.web.utils.EasyPoiStylerColorImpl;
import com.neusoft.bizcore.web.utils.JsonUtils;
import com.neusoft.neuiotms.dcom.constants.DCOMConstants;
import com.neusoft.neuiotms.dcom.converter.OperationSupportWeeklyConverter;
import com.neusoft.neuiotms.dcom.dto.InstitutionsDTO;
import com.neusoft.neuiotms.dcom.dto.OperationSupportWeeklyDTO;
import com.neusoft.neuiotms.dcom.model.OperationSupportWeekly;
import com.neusoft.neuiotms.dcom.repository.OperationSupportWeeklyRepository;
import com.neusoft.neuiotms.dcom.service.InstitutionsService;
import com.neusoft.neuiotms.dcom.service.OperationSupportWeeklyService;
import com.neusoft.neuiotms.dcom.util.PermUtils;
import com.neusoft.neuiotms.dcom.vo.OperationSupportWeeklyImportVO;
import com.neusoft.neuiotms.dcom.vo.OperationSupportWeeklyVO;
import com.neusoft.neuiotms.micro.notice.NoticeMicroService;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class OperationSupportWeeklyServiceImpl implements OperationSupportWeeklyService {

    @Autowired
    private OperationSupportWeeklyRepository weeklyRepository;
    @Autowired
    private OperationSupportWeeklyConverter weeklyConverter;
    @Autowired
    private InstitutionsService institutionsService;
    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private NoticeMicroService noticeMicroService;

    @Override
    public List<OperationSupportWeeklyDTO> index(final Searchable searchable, final UserBean userBean) {
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(userBean);
        final List<String> institutionCodeArray =
                institutions.stream().map(it -> it.getCode()).collect(Collectors.toList());
        searchable.put("institutionCodeArray", institutionCodeArray);
        if (PermUtils.isWorker(userBean)) {
            searchable.put("userCode", userBean.getUsername());
        }
        final List<OperationSupportWeekly> result = this.weeklyRepository.index(searchable);
        return this.weeklyConverter.toListDTO(result);
    }

    @Override
    public Page<OperationSupportWeeklyDTO> page(final Searchable searchable, final Pageable pageable,
            final UserBean userBean) {
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(userBean);
        final List<String> institutionCodeArray =
                institutions.stream().map(it -> it.getCode()).collect(Collectors.toList());
        searchable.put("institutionCodeArray", institutionCodeArray);
        if (PermUtils.isWorker(userBean)) {
            searchable.put("userCode", userBean.getUsername());
        }
        final Page<OperationSupportWeekly> result = this.weeklyRepository.search(pageable, searchable);
        return this.weeklyConverter.toPageDTO(result);
    }

    @Override
    public OperationSupportWeeklyDTO create(final OperationSupportWeeklyDTO dto) {

        final OperationSupportWeekly model = this.weeklyConverter.toModel(dto);
        this.weeklyRepository.save(model);
        return this.weeklyConverter.toDTO(model);
    }

    @Override
    public OperationSupportWeeklyDTO update(final Long id, final OperationSupportWeeklyDTO dto) {

        OperationSupportWeekly model;
        try {
            model = this.weeklyRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        dto.setId(model.getId());

        model = this.weeklyConverter.toModel(dto);
        this.weeklyRepository.save(model);

        return this.weeklyConverter.toDTO(model);
    }

    @Override
    public OperationSupportWeeklyDTO detail(final Long id) {

        OperationSupportWeekly model;
        try {
            model = this.weeklyRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        return this.weeklyConverter.toDTO(model);
    }

    @Override
    public void delete(final Long id) {
        this.weeklyRepository.deleteById(id);
    }

    @Override
    public void upload(final MultipartFile file, final UserBean user) {

        final ImportParams params = new ImportParams();
        params.setTitleRows(2);
        params.setHeadRows(1);
        params.setNeedVerify(true);
        ExcelImportResult<OperationSupportWeeklyVO> result = null;
        try {
            result = ExcelImportUtil.importExcelMore(file.getInputStream(), OperationSupportWeeklyVO.class, params);
        } catch (final IOException e) {
            OperationSupportWeeklyServiceImpl.log.error(e.toString());
            throw new UnitedException("导入文件失败");
        } catch (final Exception e) {
            OperationSupportWeeklyServiceImpl.log.error(e.toString());
            throw new UnitedException("导入文件失败");
        }
        if ((result != null) && result.isVerifyFail()) {
            final List<OperationSupportWeeklyVO> failList = result.getFailList();
            final StringBuffer sb = new StringBuffer();
            failList.stream().map(l -> "第" + (l.getRowNum() + 1) + "行," + l.getErrorMsg() + "! \n").forEach(sb::append);

            if (failList.size() > 0) {
                throw new UnitedException(sb.toString());
            }

        }
        final List<OperationSupportWeekly> models = new ArrayList<>();
        final List<OperationSupportWeeklyVO> list = result.getList();

        list.stream().map(s -> this.weeklyConverter.toModel(s)).filter(k -> null != k).forEach(models::add);

        if (!models.isEmpty()) {
            this.weeklyRepository.saveAll(models);
        }

    }

    @Override
    public Workbook export(final Searchable searchable, final UserBean userBean) {

        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(userBean);
        final List<String> institutionCodeArray =
                institutions.stream().map(it -> it.getCode()).collect(Collectors.toList());
        searchable.put("institutionCodeArray", institutionCodeArray);

        final List<OperationSupportWeekly> models = this.weeklyRepository.index(searchable);
        final List<OperationSupportWeeklyVO> list = new ArrayList<>(models.size() == 0 ? 1 : models.size());

        for (int j = 0; j < models.size(); j++) {
            final OperationSupportWeekly t = models.get(j);
            final OperationSupportWeeklyVO vo = this.weeklyConverter.toVO(t);
            vo.setIndex((j + 1) + "");
            list.add(vo);
        }

        final ExportParams param = new ExportParams("海南省省直单位信息化基础设施移交及运维服务 - 运维支撑服务周报", "运维支撑服务周报", ExcelType.XSSF);
        param.setStyle(EasyPoiStylerColorImpl.class);
        return ExcelExportUtil.exportExcel(param, OperationSupportWeeklyVO.class, list);
    }

    @Override
    public List<InstitutionsDTO> unstartdInstitutions() {
        final List<InstitutionsDTO> institutions = this.institutionsService.index(new Searchable());
        final Calendar c = Calendar.getInstance();
        int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (dayOfWeek == 0) {
            dayOfWeek = 7;
        }
        c.add(Calendar.DATE, -dayOfWeek + 1);

        final Date start = DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.DAY_OF_MONTH);

        final List<OperationSupportWeekly> list = this.weeklyRepository.findAllByWeekDate(start);
        return institutions.stream()
                .filter(it -> list.stream().filter(w -> it.getCode().equals(w.getCompanyCode())).count() == 0)
                .collect(Collectors.toList());
    }

    @Override
    public Map<String, Long> reportStats() {
        final List<InstitutionsDTO> institutions = this.institutionsService.index(new Searchable());

        final Calendar c = Calendar.getInstance();
        int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (dayOfWeek == 0) {
            dayOfWeek = 7;
        }
        c.add(Calendar.DATE, -dayOfWeek + 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        final Date start = c.getTime();

        final List<OperationSupportWeekly> list = this.weeklyRepository.findAllByWeekDate(start);
        final Map<String, Long> result = Maps.newHashMap();
        final long reportedCount = list.stream().map(it -> it.getCompanyCode()).distinct().count();
        result.put("已提交", reportedCount);
        result.put("未提交", institutions.size() - reportedCount);

        return result;
    }

    @Override
    public Workbook template(final UserBean user) {
        final List<OperationSupportWeeklyImportVO> list = new ArrayList<>();
        final Workbook workbook = ExcelExportUtil.exportExcel(
                new ExportParams(null, DCOMConstants.TRANSFER_IMPORT_TEMPLATE_NAME, ExcelType.XSSF),
                OperationSupportWeeklyImportVO.class,
                list);

        //下拉框候选数据
        final List<Map<String, Object>> constraint = new ArrayList<>();
        //单位
        final List<String> constraintList = new ArrayList<>();
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(user);
        institutions.stream().map(t -> {
            return t.getName() + DCOMConstants.DICTIONARY_SPLITOR + t.getCode();
        }).forEach(constraintList::add);
        final Map<String, Object> map = new HashMap<>();
        map.put("list", constraintList);
        map.put("col", DCOMConstants.OPERATION_WEEKLY_UNIT_NO);
        constraint.add(map);
        //报告人
        final List<String> reporter =
                Lists.newArrayList(user.getName() + DCOMConstants.DICTIONARY_SPLITOR + user.getUsername());
        final Map<String, Object> mapReporter = new HashMap<>();
        mapReporter.put("list", reporter);
        mapReporter.put("col", DCOMConstants.OPERATION_WEEKLY_REPORTER_NO);
        constraint.add(mapReporter);
        EasyPoiExcelUtil.setConstraint(constraint, workbook, DCOMConstants.ASSET_START_CONSTRAINT_ROW_FIRST,
                DCOMConstants.ASSET_CONSTRAINT_ROW_65535);
        return workbook;
    }

    @Override
    @Transactional
    public void importExcel(final InputStream fileStream, final UserBean user) {
        final StringBuilder errorMsg = new StringBuilder();
        final ImportParams params = new ImportParams();
        params.setHeadRows(1);
        params.setNeedVerify(true);
        try {
            final ExcelImportResult<OperationSupportWeeklyImportVO> result =
                    ExcelImportUtil.importExcelMore(fileStream, OperationSupportWeeklyImportVO.class, params);
            if (result.getList().isEmpty() && result.getFailList().isEmpty()) {
                throw new UnitedException("运维支撑周报上传数据为空");
            }
            if (result.isVerifyFail()) {
                final List<OperationSupportWeeklyImportVO> failList = result.getFailList();
                failList.forEach(l -> {
                    errorMsg.append("第" + (l.getRowNum() + 1) + "行," + l.getErrorMsg() + "! \n");
                });
            }
            if (errorMsg.length() > 0) {
                OperationSupportWeeklyServiceImpl.log.error(errorMsg.toString());
                throw new UnitedException(errorMsg.toString(), new Object[] {});
            }

            final List<OperationSupportWeeklyImportVO> saveList = result.getList();
            final String currentUser = user.getName() + DCOMConstants.DICTIONARY_SPLITOR + user.getUsername();
            final List<OperationSupportWeeklyImportVO> person = saveList.stream().filter(t -> {
                return !t.getUser().equals(currentUser);
            }).collect(Collectors.toList());
            if (person.size() > 0) {
                throw new UnitedException("导入运维支撑周报处理人必须为当前登录用户：" + user.getName());
            }
            final List<OperationSupportWeekly> models = new ArrayList<>();
            for (int i = 0; i < saveList.size(); i++) {
                models.add(this.weeklyConverter.toModel(saveList.get(i)));
            }
            if (!models.isEmpty() && (errorMsg.length() == 0)) {
                this.weeklyRepository.saveAll(models);
            }

        } catch (final Exception e) {
            e.printStackTrace();
            OperationSupportWeeklyServiceImpl.log.error("运维支撑周报上传失败", e);
            throw new UnitedException(e.getMessage().toString());
        } finally {
            try {
                fileStream.close();
            } catch (final IOException e) {
                e.printStackTrace();
                throw new UnitedException(e.getMessage().toString());
            }
        }

    }

    @SuppressWarnings("unchecked")
    @Override
    public Page<Map<String, Object>> report(Pageable pageable, final String username, final Date start,
            final Date end) {

        final String countSql = this.buildTotalSql(username, start, end);
        OperationSupportWeeklyServiceImpl.log.info(countSql);
        final Query query1 = this.entityManager.createNativeQuery(countSql);
        query1.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result1 = query1.getResultList();
        int total = 0;
        if (result1.size() > 0) {
            total = Integer.parseInt(String.valueOf(result1.get(0).get("count")));
        }

        final String sql = this.buildQuerySql(pageable, username, start, end);
        OperationSupportWeeklyServiceImpl.log.info(sql);
        final Query query = this.entityManager.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result = query.getResultList();

        return new PageImpl<>(result, pageable, total);
    }

    @SuppressWarnings("unchecked")
    @Override
    public Workbook exportReport(final String username, final Date start, final Date end) {
        final String sql = this.buildExportSql(username, start, end);
        OperationSupportWeeklyServiceImpl.log.info(sql);
        final Query query = this.entityManager.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result = query.getResultList();

        final List<WeekReportVO> list = new ArrayList<>(result.size() == 0 ? 1 : result.size());
        result.stream().map(t -> {
            final WeekReportVO vo = new WeekReportVO();
            vo.setUser_name(t.get("user_name"));
            vo.setTotal_times(t.get("total_times"));
            return vo;
        }).forEach(list::add);

        final ExportParams param =
                new ExportParams("周报统计", "周报统计", ExcelType.XSSF);
        param.setStyle(EasyPoiStylerColorImpl.class);
        return ExcelExportUtil.exportExcel(param, WeekReportVO.class, list);
    }

    private String buildTotalSql(final String username, final Date start, final Date end) {
        final String statement = " count(*) as count ";
        return this.buildSql(username, start, end, statement);
    }

    private String buildQuerySql(Pageable pageable, final String username, final Date start, final Date end) {
        final String statement = " * ";
        String noPageSql = this.buildSql(username, start, end, statement);
        noPageSql += " limit " + pageable.getPageSize() + " offset " + pageable.getOffset();

        return noPageSql;
    }

    private String buildExportSql(final String username, final Date start, final Date end) {
        final String statement = " * ";
        return this.buildSql(username, start, end, statement);
    }

    private String buildSql(final String username, final Date start, final Date end, String statement) {
        String where = "";
        if (StringUtils.isNotBlank(username)) {
            where += "and user_code='" + username + "' ";
        }
        if (null != start) {
            where += "and week_date>='" + DateFormatUtils.format(start, "yyyy-MM-dd") + "' ";
        }
        if (null != end) {
            final Calendar c = Calendar.getInstance();
            c.setTime(end);
            c.add(Calendar.DAY_OF_MONTH, 1);
            where += "and week_date<'" + DateFormatUtils.format(c.getTime(), "yyyy-MM-dd") + "' ";
        }

        String sql = "SELECT "
                + statement
                + " FROM ( "
                + " SELECT "
                + " user_name, "
                + "  count(*) AS total_times "
                + " FROM "
                + "  neuiot_operation_support_weekly "
                + "WHERE "
                + "  user_name IS NOT NULL "
                + "  AND user_name <> '' "
                + "  %s "
                + "GROUP BY "
                + "  user_name "
                + ") t ";

        sql = String.format(sql, where, where, where, where);

        return sql;
    }

    @Setter
    @Getter
    static class WeekReportVO {

        @Excel(name = "人员", orderNum = "0")
        private Object user_name;

        @Excel(name = "填报次数", orderNum = "1")
        private Object total_times;

    }

    /**
     * 运维服务周报以厅局未单位
     */
    @Override
    public void checkReportNotice() {
        final List<InstitutionsDTO> unstartdInstitutions = this.unstartdInstitutions();
        final List<String> users = Lists.newArrayList();
        unstartdInstitutions.forEach(it -> {
            final List<String> usernameArray =
                    it.getResidents().stream().map(r -> r.getUsername()).collect(Collectors.toList());
            users.addAll(usernameArray);
        });
        if (users.size() == 0) {
            OperationSupportWeeklyServiceImpl.log.info("today's week report is all reported, need not notice.");
            return;
        }
        // 周报和月报不判断节假日
        //        final List<Holiday> lists = this.holidayRepository.findByDate(new Date());
        //        if (lists.size() > 0) {
        //            OperationSupportWeeklyServiceImpl.log.info("今天是节假日,不做日提醒");
        //            return;
        //        }

        final NoticeRecordDTO notice = new NoticeRecordDTO();
        notice.setCreateTime(new Date());
        notice.setAppCode("dcom");
        notice.setType(Lists.newArrayList(NoticeConsts.INFORM_TYPE_NOTICE, NoticeConsts.INFORM_TYPE_DINGDING));
        notice.setTitle("运维服务周报提醒");
        final NoticeRecordSystemDTO toSystem = new NoticeRecordSystemDTO();
        toSystem.setNoticeType("周报提醒");
        toSystem.setNoticeRoute("hn_operationSupportWeekly");
        toSystem.setUsers(users);
        toSystem.setContent("请及时填写运维服务周报");

        notice.setSystem(toSystem);

        final NoticeDingDTO toDing = new NoticeDingDTO();
        toDing.setMsgtype("link");
        toDing.setUsers(users);
        final Map<String, String> linkParams = Maps.newHashMap();
        linkParams.put("messageUrl", "eapp://pages/week/week");
        linkParams.put("picUrl", "@lALPDeRETJL6S3XMtMy0");
        linkParams.put("title", "周报提醒");
        linkParams.put("text", "请填写本周运维服务周报");
        toDing.setJsonBody(JsonUtils.pojoToJson(linkParams));
        notice.setDing(toDing);

        this.noticeMicroService.createNotification(JsonUtils.pojoToJson(notice));
    }

    public static void main(String[] args) {
        System.err.println(DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.DAY_OF_MONTH));
    }
}
